import { makeAutoObservable } from 'mobx';
import { v4 as uuidv4 } from 'uuid';

class Log {
    id = uuidv4();

    index = null;

    commandId = null;

    message = null;

    description = null;

    status = null;

    channel = null;

    isNotice = false;

    constructor(message, status) {
        this.message = message;
        this.status = status;
        this.timestamp = new Date();
        makeAutoObservable(this, {}, { autoBind: true });
    }

    setIndex(index) {
        this.index = index;
    }

    setCommandId(commandId) {
        this.commandId = commandId;
    }

    setMessage(message) {
        this.message = message;
    }

    setDescription(desc) {
        this.description = desc;
    }

    setStatus(status) {
        this.status = status;
    }

    setChannel(channel) {
        this.channel = channel;
    }

    setNotice() {
        this.isNotice = true;
    }

    setTime() {
        this.time = new Date().toLocaleString().trim();
    }

    isEqual(otherLog) {
        return this.message === otherLog.message && this.commandId === otherLog.commandId;
    }

    get _str() {
        return `${this.index ? this.index + '. ' : ''}${this.message}${this.description ? '\n\r  ' + this.description : ''}`;
    }

    toString() {
        return this._str;
    }
}

export const LogTypes = {
    Running: 'running',
    Success: 'success',
    Warning: 'warn',
    Error: 'error',
    Failure: 'failure',
    Undetermined: 'undetermined',
    Awaiting: 'awaiting',
};

class Output {
    logs = [];

    log(log) {
        this.logs.push(log);
    }

    clear() {
        this.logs.clear();
    }

    print(std) {
        if (!std) {
            return this.logs.join('\r\n');
        } else if (std === 1) {
            return this.logs.filter((log) => log.status === LogTypes.Success).join('\r\n');
        } else if (std === 2) {
            return this.logs.filter((log) => log.status !== LogTypes.Success).join('\r\n');
        } else {
            throw new Error('No such standard');
        }
    }
}

export const output = new Output();

export const Channels = {
    PLAYBACK: 'playback',
    SYSTEM: 'sys',
};

export class Logger {
    constructor(channel = Channels.SYSTEM) {
        this.channel = channel;

        this.log = this.log.bind(this, this.channel);
        this.warn = this.warn.bind(this);
        this.error = this.error.bind(this);
        makeAutoObservable(this, {}, { autoBind: true });
    }

    log(channel, log) {
        if (typeof log === 'string') {
            log = new Log(log);
        }
        log.setChannel(channel);
        output.log(log);

        return log;
    }

    warn(log) {
        const warnLog = this.log(typeof log === 'string' ? `Warning ${log}` : log);
        warnLog.setStatus(LogTypes.Warning);
    }

    error(log) {
        const errorLog = this.log(log);
        errorLog.setStatus(LogTypes.Error);
    }

    get(channel) {
        return new Logger(channel);
    }

    clearLogs() {
        output.clear();
    }

    printLogs(std) {
        return output.print(std);
    }
}

export default window._logger;
